<?php


namespace App\Models;


use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

trait LogTrait
{
    protected $log;

    /**
     * @var int
     * 记录类型的键
     */
    protected $logType = 0;

    /**
     * @var string[]
     * 记录类型
     */
    protected $logTypeArr = ['新增', '更新', '删除'];

    /**
     * @var string[]
     * 记录字段，*为全部记录
     */
    protected $logEnable = ['*'];

    /**
     * @var array
     * 不记录字段
     */
    protected $logUnable = ['auth_token', 'password'];

    /**
     * @var bool
     * 是否不记录
     */
    public $unLog = false;

    protected static function boot()
    {
        parent::boot();
        /**
         *新增
         */
        self::created(function (Model $model) {
            if (!$model->unLog) {
                $model->logType = 0;
                $model->log()->header()->toLog();
            }
            $model->clearCache();
        });
        /**
         * 更新完成
         * 清除缓存并释放锁
         */
        self::updated(function (Model $model) {
            if (!$model->unLog) {
                $model->logType = 1;
                $model->log()->header()->toLog();
            }
            $model->clearCache();
        });
        /**
         * 删除成功
         * 清除缓存并释放锁
         */
        self::deleted(function (Model $model) {
            if (!$model->unLog) {
                $model->logType = 2;
                $model->log()->header()->toLog();
            }
            $model->clearCache();
        });
    }

    /**
     * @param $bool
     * @return $this
     */
    public function unLog($bool)
    {
        $this->unLog = $bool;
        return $this;
    }

    /**
     * @return int|string
     * 获取操作类别
     */
    public function getLogType()
    {
        return isset($this->logTypeArr[$this->logType]) ? $this->logTypeArr[$this->logType] : $this->logType;
    }

    /**
     * @param array $logTypeArr
     * @return $this
     * 设置操作类别
     */
    protected function logTypeArr(array $logTypeArr)
    {
        $this->logTypeArr = $logTypeArr;
        return $this;
    }

    /**
     * @param $key
     * @return bool
     * 判断是否记录
     */
    public function isLogEnable($key)
    {
        if (in_array($key, $this->logUnable)) {
            return false;
        }

        if (in_array('*', $this->logEnable) || in_array($key, $this->logEnable)) {
            return true;
        }

        return false;
    }

    /**
     * @param array $attributes
     * @return array
     * 筛选需要记录的字段
     */
    protected function logEnableFromArray(array $attributes)
    {
        if (count($this->logEnable) > 0 && !$this->unLog) {
            if (in_array('*', $this->logEnable)) {
                return $attributes;
            }
            return array_intersect_key($attributes, array_flip($this->logEnable));
        }

        return $attributes;
    }

    /**
     * @return $this
     * 获取记录内容
     */
    public function log()
    {
        $this->log = collect();
        $dirty = $this->getDirty();
        if ($this->logType == 2 && !$this->isDirty('deleted_at')) {//非软删除情况下
            $dirty = $this->getAttributes();
        }
        unset($dirty[$this->primaryKey]);//不记录主键
        unset($dirty['created_at']);//不记录新增时间
        unset($dirty['updated_at']);//不记录更新时间
        unset($dirty['deleted_at']);//不记录删除时间
        foreach ($this->logEnableFromArray($dirty) as $key => $value) {
            $key = $this->removeTableFromKey($key);
            if ($this->islogEnable($key)) {
                $this->description($key);
            }
        }
        return $this;
    }

    /**
     * @param string $before
     * @param string $after
     * @return $this
     * 拼接记录操作表
     */
    protected function header($before = '', $after = '')
    {
        $collection = collect();
        $collection->push($before);
        $collection->push($this->getKey());
        $collection->push($after);
        $collection = $collection->map(function ($item) {
            if (!empty($item)) {
                return '「' . $item . '」';
            }
        });
        $table = trans('models/' . $this->getTable());
        $name = $table['table'] ?? $this->getTable();
        if ($this->log->count() > 0)
            $this->log->prepend($this->getLogType() . $name . $collection->implode(''));
        return $this;
    }

    /**
     * @param $key
     * @return $this
     * 拼接记录
     */
    protected function description($key)
    {
        $column = trans('models/' . $this->getTable() . '.' . $key);
        $info = $column['info'] ?? $key;
        if (isset($column['fun'])) {
            $fun = $column['fun'];
            $from = $fun($this->getFrom($key));
            $to = $fun($this->getTo($key));
        } else {
            $from = $this->getFrom($key);
            $to = $this->getTo($key);
        }
        if ($this->logType == 0 || ($this->logType == 2 && !$this->isDirty('deleted_at'))) {//非软删除
            $this->log->push($info . '「' . $this->toString($to) . '」');
        } else {
            $this->log->push($info . '由「' . $this->toString($from) . '」变成「' . $this->toString($to) . '」');
        }
        return $this;
    }

    /**
     * 记录到数据库
     */
    protected function toLog()
    {
        if ($this->log->count() > 0) {
            $log = new AdminLog();
            $log->admin_id = intval(auth()->id());
            $log->ip = request()->ip();
            $header = $this->log->shift();
            $log->remarks = $header . ':' . $this->log->implode(';');
            $log->table_name = $this->getTable();
            $log->save();
        }
    }

    /**
     * @param $key
     * @return mixed
     * 原值
     */
    protected function getFrom($key)
    {
        if ($this->hasGetMutator($key)) {
            $from = $this->{'get' . Str::studly($key) . 'Attribute'}($this->getOriginal($key));
        } else {
            $from = $this->getOriginal($key);
        }
        return $from;
    }

    /**
     * @param $key
     * @return mixed
     * 改变后的值
     */
    protected function getTo($key)
    {
        $to = $this->$key;
        return $to;
    }

    /**
     * @param $value
     * @return false|string
     * 转移成字符串
     */
    protected function toString($value)
    {
        if ($value instanceof Carbon) {
            $value = $value->format('Y-m-d H:i:s');
        } elseif (is_array($value) || is_object($value)) {
            $value = response()->json($value)->getContent();
        }
        return $value;
    }

    /**
     * 清除缓存
     */
    protected function clearCache()
    {
        Cache::store('pc')->tags([$this->getTable()])->flush();
        Cache::store('app')->tags([$this->getTable()])->flush();
        Cache::store('ywy')->tags([$this->getTable()])->flush();
        Cache::tags([$this->getTable()])->flush();
    }
}
